import subprocess
import time
from threading import Thread
from typing import Tuple, List, Optional, Union, Literal, Any, Dict
from PopenQueue import PopenQueue

class KataGoGtp:
    def __init__(self, katago_path: str, config_path: str, model_path: str, additional_args: List[str] = []):
        self.ok = False
        self.run = True
        katago = subprocess.Popen(
            [katago_path, "gtp", "-config", config_path, "-model", model_path, *additional_args],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        self.katago = katago
        self.popenQueue = PopenQueue(self.katago)
        self.popenQueue.StartReadingThread()
        self.stderrthread = Thread(target=KataGoGtp.__printErrorThread, args=(self,))
        self.stderrthread.start()

    '''
    katago 左下坐标
    foxwq 左上坐标
    '''
    def pos_to_kt_move(self, x: int, y: int) -> str:
        return "ABCDEFGHJKLMNOPQRSTUVWXYZ"[x] + str(19-y)

    def kt_move_to_pos(self, m: str) -> Tuple[int,int]:
        return ('ABCDEFGHJKLMNOPQRSTUVWXYZ'.index(m[0:1]), 19-int(m[1:]))

    def __printErrorThread(self):
        while self.run:
            if not self.popenQueue.err_empty():
                line=self.popenQueue.err_get()
                if line.startswith('GTP ready'):
                    self.ok=True
                print("KataGo StdErr: ", line, end='\n')
            time.sleep(0.2)

    def boardsize(self, size):
        self.katago.stdin.write(("boardsize "+str(size)+"\n").encode())
        self.katago.stdin.flush()
        l=self.read_line()
        if l.startswith('='):
            print('Set boardsize '+str(size))

    def close(self):
        self.katago.stdin.write(('quit\n').encode())
        self.katago.stdin.flush()
        l=self.read_line()
        if l.startswith('='):
            print('Closeed')
        self.run=False
    
    def play_pos(self, color: str, x: int, y: int):
        self.play(color, self.pos_to_kt_move(x, y))

    def play(self, color: str, move: str):
        self.popenQueue.out_clear()
        self.katago.stdin.write(('play '+color+' '+move+'\n').encode())
        self.katago.stdin.flush()
        l=self.read_line()
        mv = self.kt_move_to_pos(move)
        if l=='=':
            print('Played '+color+' '+str(mv))
        else:
            print('Fail: '+l)

    def clear_board(self):
        self.katago.stdin.write(("clear_board\n").encode())
        self.katago.stdin.flush()
        l=self.read_line()
        if l.startswith('='):
            print('Clear board')

    def genmove(self, color: str):
        self.popenQueue.out_clear()
        t = time.time()
        self.katago.stdin.write(('genmove '+color+'\n').encode())
        self.katago.stdin.flush()
        l=self.read_line()
        if l.startswith('='):
            coast=time.time() - t
            mv = self.kt_move_to_pos(l[2:].strip())
            print('Genmove '+color+' '+str(mv)+' coast:'+str(coast))
            return mv
        else:
            return None

    def showboard(self):
        self.popenQueue.out_clear()
        self.katago.stdin.write(('showboard\n').encode())
        self.katago.stdin.flush()
        line=''
        while not line.startswith('W stones captured'):
            time.sleep(0.1)
            if not self.popenQueue.out_empty():
                line = self.popenQueue.out_get()
                print(line)


    def read_line(self):
        line = ''
        while line=='':
            if self.katago.poll():
                time.sleep(1)
                raise Exception("Unexpected katago exit")
            time.sleep(0.1)
            if not self.popenQueue.out_empty():
                line=self.popenQueue.out_get()
        return line


def test_gtp():
    eg = 'bs'
    katago = KataGoGtp('D:\\katago'+eg+'\\katago.exe', 'D:\\katago'+eg+'\\g.cfg', 'D:\\katago'+eg+'\\model.bin.gz')
    while not katago.ok:
        time.sleep(1)
    katago.boardsize(19)
    katago.play_pos('b', 3, 3)
    katago.play_pos('w', 15, 3)
    katago.play_pos('b', 15, 15)
    katago.genmove('w')
    katago.showboard()
    katago.close()

if __name__ == "__main__":
    test_gtp()